﻿using System;
using System.Net;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
using System.Xml;
using MouseExtender.WebSite.Logic.DataEntities;

namespace MouseExtender.WebSite.Logic.Managers
{
    public class UpdateManager
    {
        private const string ReleaseRssUrl = @"http://me.codeplex.com/Project/ProjectRss.aspx?ProjectRSSFeed=codeplex%3a%2f%2frelease%2fme";
        private const string ReleaseDescriptionXPath = LatestReleaseXPath + "description";

        private const string LatestReleaseXPath = "/rss/channel/item[{0}]/";
        private const string ReleaseUrlXPath = LatestReleaseXPath + "link";

        private const string VersionRegEx = @"Version.*(?<" + GroupName + @">\d+\.\d+\.\d+\.\d+)";
        private const string GroupName = "version";

        /// <summary>
        /// Checks if new version of Application is available for download.
        /// </summary>
        /// <param name="latestReleaseUrl">New version package download url.</param>
        /// <returns>true if newer version is availble.</returns>
        public UpdateInfoEntity CheckForUpdates(Version requestAppVersion)
        {
            XmlDocument rss = (XmlDocument)HttpRuntime.Cache[Constants.CacheKeyCodeplexReleaseRss];
            if (rss == null)
            {
                rss = DownloadReleaseRss();
                HttpRuntime.Cache.Add(Constants.CacheKeyCodeplexReleaseRss, rss, null, DateTime.Now.AddMinutes(10), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
            }

            int downloadableReleasIndex = 0;
            Version latestVersion = GetLatestVersion(rss, out downloadableReleasIndex);
            bool newVersionExists = IsNewerVersionAvailable(latestVersion, requestAppVersion);
            Uri latestReleaseUrl = GetLatestReleseUri(rss, downloadableReleasIndex);

            var updateInfo = new UpdateInfoEntity()
            {
                LatestReleaseUrl = latestReleaseUrl,
                LatestVersion = latestVersion,
                NewVersionExists = newVersionExists,
            };

            return updateInfo;
        }

        /// <summary>
        /// Downloads Rss from codeplex.com and parses it to XmlDocument
        /// </summary>
        /// <returns>XmlDocument</returns>
        private XmlDocument DownloadReleaseRss()
        {
            //TODO:insert cache here
            using (WebClient rssClient = CreateWebClient())
            {
                string rss = rssClient.DownloadString(ReleaseRssUrl);
                XmlDocument rssXml = new XmlDocument();
                rssXml.LoadXml(rss);
                return rssXml;
            }
        }

        private static WebClient CreateWebClient()
        {
            WebClient client = new WebClient();
            client.UseDefaultCredentials = true;
            return client;
        }

        private static Version GetLatestVersion(XmlDocument rss, out int itemIndex)
        {
            itemIndex = 0;
            string versionStr = String.Empty;
            do
            {
                itemIndex++;
                string xPath = String.Format(ReleaseDescriptionXPath, itemIndex);
                XmlNode latestUpdate = rss.SelectSingleNode(xPath);
                if (latestUpdate == null)
                    break;

                string description = latestUpdate.InnerText;
                Regex regex = new Regex(VersionRegEx);
                Match versionMatch = regex.Match(description);
                versionStr = versionMatch.Groups[GroupName].Value;
            }
            while (String.IsNullOrEmpty(versionStr));

            Version latestVersion = new Version(versionStr);
            return latestVersion;
        }

        private static bool IsNewerVersionAvailable(Version latestVersion, Version currentVersion)
        {
            bool isNewerVersionAvailable = false;
            if (latestVersion > currentVersion)
                isNewerVersionAvailable = true;
            return isNewerVersionAvailable;
        }

        private static Uri GetLatestReleseUri(XmlDocument rss, int itemIndex)
        {
            string xPath = String.Format(ReleaseUrlXPath, itemIndex);
            XmlNode urlElement = rss.SelectSingleNode(xPath);
            string urlStr = urlElement.InnerText;

            Uri link = new Uri(urlStr);
            return link;
        }
    }
}
